// 日志相关单元测试
#include <iostream>
#include <thread>

#include "xlcomm/base/async_log.h"
#include "xlcomm/base/date_time.h"
#include "xlcomm/base/file.h"
#include "xlcomm/base/fix_buffer.h"
#include "xlcomm/base/logger.h"
#include "xlcomm/base/sync_log.h"

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include "boost/test/unit_test.hpp"

#define CHECK_ABORT 0

#if (CHECK_ABORT == 1)
BOOST_AUTO_TEST_CASE(XLCOMM_ASSERT0_test) {
  std::cout << "-------------XLCOMM_ASSERT0\n";
  XLCOMM_ASSERT0(1 == 2);
}

BOOST_AUTO_TEST_CASE(XLCOMM_ASSERT_test) {
  std::cout << "-------------XLCOMM_ASSERT\n";
  XLCOMM_ASSERT(1 == 2);
}
#endif  // CHECK_ABORT

BOOST_AUTO_TEST_CASE(xlcomm_define) {
  std::cout << "-------------xlcomm_define\n";
  int arr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
  xlcomm::MemZero(arr, sizeof arr);
  for (int i = 0; i < 8; ++i) {
    BOOST_CHECK(arr[i] == 0);
  }

  char str[10];
  xlcomm::Strncpy(str, "hello67890", sizeof str);
  BOOST_CHECK(strlen(str) == 9);
  BOOST_CHECK(strncmp(str, "hello6789", 10) == 0);

  char* p = new char[10];
  xlcomm::Strncpy(p, "hello67890", 10);
  BOOST_CHECK(strlen(p) == 9);
  BOOST_CHECK(strncmp(p, "hello6789", 10) == 0);
}

void LongWork(int count) {
  int64_t sum = 0;
  for (int i = 0; i < count; ++i) {
    sum += i * 987;
  }
  std::cout << "LongWork sum:" << sum << std::endl;
}

BOOST_AUTO_TEST_CASE(DateTime) {
  std::cout << "-------------DateTime\n";
  xlcomm::CommTime now = xlcomm::DateTime::Now();
  std::cout << xlcomm::DateTime::Format(now) << std::endl;

  xlcomm::Timestamp timestamp = xlcomm::DateTime::NowTimestamp();
  std::cout << "timestamp:" << timestamp << std::endl;

  xlcomm::Timing t;
  t.Start();
  LongWork(100000);
  std::cout << "duration:" << t.Stop() << " us" << std::endl;
}

BOOST_AUTO_TEST_CASE(File_o) {
  std::cout << "-------------File_o\n";
  xlcomm::File file("File_o.log", "r");
}

BOOST_AUTO_TEST_CASE(File_w) {
  std::cout << "-------------File_w\n";
  char filename[] = "file_w.log";
  xlcomm::File file;
  {
    // Length
    file.Open(filename, "w+");
    char str[] = "hello";
    file.Write(str, strlen(str));
    char str2[] = "file";
    file.Write(str2, strlen(str2));
    BOOST_CHECK(file.Length() == 9);
    file.Close();
  }
  // 连续写
  file.Open(filename, "w+");
  char str[] = "ab";
  file.Write(str, strlen(str));
  char str2[] = "cd";
  file.Write(str2, strlen(str2));
  BOOST_CHECK(file.Length() == 4);

  // 全部读出
  file.Seek(0, SEEK_SET);
  char out[32] = {0};
  file.Read(out, sizeof(out));
  BOOST_CHECK(strcmp(out, "abcd") == 0);
  std::cout << out << std::endl;

  // 读一部分
  file.Seek(0, SEEK_SET);
  char out2[3] = {0};
  file.Read(out2, sizeof(out2) - 1);
  BOOST_CHECK(strcmp(out2, "ab") == 0);
  std::cout << out2 << std::endl;
  xlcomm::Logger::SetLogLevel(xlcomm::Logger::kTrace);

  // 加载
  std::string content;
  file.Load(content);
  BOOST_CHECK(strcmp(content.c_str(), "abcd") == 0);
  BOOST_CHECK(content.length() == content.size());

  // 从头写一部分
  file.Seek(0, SEEK_SET);
  char aa[] = "aa";
  file.Write(aa, strlen(aa));
  BOOST_CHECK(file.Length() == 4);
}

BOOST_AUTO_TEST_CASE(File_a) {
  std::cout << "-------------File_a\n";
  char filename[] = "file_a.log";
  xlcomm::File file;
  // 清空所有内容
  file.Open(filename, "w");
  file.Close();

  // 写入'\0'
  file.Open(filename, "a+");
  char hello[] = "hello";
  file.Write(hello, strlen(hello) + 1);
  BOOST_CHECK(file.Length() == 6);
  file.Close();

  // 追加方式写
  file.Open(filename, "a+");
  char nihao[] = "nihao";
  file.Write(nihao, strlen(nihao) + 1);
  BOOST_CHECK(file.Length() == 12);

  // 测试换行
  std::string newline = "\nnewline\n";
  BOOST_CHECK(newline.length() == 9);
  file.Write(newline.c_str(), newline.length());

  std::string content;
  file.Load(content);
  std::cout << "--string:" << content;  // string能输出全
  std::cout << "--string.c_str():"
            << content.c_str();  // string.c_str()不能输出全

  file.Seek(0, SEEK_SET);
  char buff[32] = {0};
  file.Read(buff, sizeof(buff));
  std::cout << "--char[]:" << buff;  // char[]遇到\0结束输出

  std::cout << "--file.Length():" << file.Length() << std::endl;
#ifdef _WIN32
  // windows写到文件里换行是\r\n
  BOOST_CHECK(file.Length() == 12 + 9 + 2);
#else
  // linux写到文件里换行是\n
  BOOST_CHECK(file.Length() == 12 + 9);
#endif  // _WIN32
  // 再读出来换行都是\n
  BOOST_CHECK(content.length() == 12 + 9);
}

class A {
 public:
  void TestClassError() { LOG_ERROR << "has class name?"; }
};

void testLog(int wait_second = 0) {
  LOG_DEBUG << "this is debug";
  LOG_INFO << "this is info";
  LOG_WARN << "this is warn";
  LOG_ERROR << "this is error";
  A a;
  a.TestClassError();
  xlcomm::Logger::SetLogLevel(xlcomm::Logger::kTrace);
  LOG_TRACE << "this is trace " << 0;
  LOG_DEBUG << "this is debug " << 1;

  // [CAUTION] do not write:
  // bool good = true;
  // if (good)
  //   LOG_INFO << "Good news";
  // else
  //   LOG_WARN << "Bad news";

  // should write:
  bool good = true;
  if (good) {
    LOG_INFO << "Good news";
  } else {
    LOG_WARN << "Bad news";
  }

  std::thread thread1([]() { LOG_INFO << "log thread1 id"; });
  std::thread thread2([]() { LOG_INFO << "log thread2 id"; });
  thread1.join();
  thread2.join();

  std::this_thread::sleep_for(std::chrono::seconds(wait_second));

  char str1[] = "hello char*";
  char* str2 = str1;
  std::string str3 = ", hello string";
  unsigned char str4[] = ", hello unsigned char";
  LOG_INFO << str2 << str3 << str4;

  char ch1 = 'a';
  unsigned char ch2 = 'b';
  LOG_INFO << ch1 << ' ' << ch2;

  short sh1 = static_cast<short>(1 << 15);
  unsigned short sh2 = static_cast<unsigned short>(1 << 15);
  LOG_INFO << sh1 << ' ' << sh2;

  int n1 = static_cast<int>(1LL << 31);
  unsigned int n2 = static_cast<unsigned int>(1LL << 31);
  LOG_INFO << n1 << ' ' << n2;

  long l1 = static_cast<long>(1LL << 31);
  unsigned long l2 = static_cast<unsigned long>(1LL << 31);
  LOG_INFO << l1 << ' ' << l2;

  long long ll1 = 1LL << 63;
  unsigned long long ll2 = 1LL << 63;
  LOG_INFO << ll1 << ' ' << ll2;

  int* ptr = &n1;
  LOG_INFO << ptr;

#if (CHECK_ABORT == 1)
  LOG_FATAL << "this is fatal";
#endif  // CHECK_ABORT
}

BOOST_AUTO_TEST_CASE(Logger) {
  std::cout << "-------------Logger\n";
  testLog();
}

static xlcomm::SyncLog g_sync_log("sync_log", 256, true);
void SyncLogOutput(const char* msg, size_t len) { g_sync_log.Append(msg, len); }
void SyncLogFlush() { g_sync_log.Flush(); }
BOOST_AUTO_TEST_CASE(SyncLog) {
  std::cout << "-------------SyncLog\n";
  xlcomm::Logger::SetOutputFunc(SyncLogOutput);
  xlcomm::Logger::SetFlushFunc(SyncLogFlush);
  testLog(1);
}

static xlcomm::SyncLog g_sync_log2("sync_log2");
void SyncLogOutput2(const char* msg, size_t len) {
  g_sync_log2.Append(msg, len);
}
void SyncLogFlush2() { g_sync_log2.Flush(); }
BOOST_AUTO_TEST_CASE(SyncLog2) {
  std::cout << "-------------SyncLog2\n";
  xlcomm::Logger::SetOutputFunc(SyncLogOutput2);
  xlcomm::Logger::SetFlushFunc(SyncLogFlush2);
  testLog(1);
}

static xlcomm::AsyncLog g_async_log("async_log", 256);
void AsyncLogOutput(const char* msg, size_t len) {
  g_async_log.Append(msg, len);
}
void AsyncLogFlush() { g_async_log.Flush(); }
BOOST_AUTO_TEST_CASE(AsyncLog) {
  std::cout << "-------------AsyncLog\n";
  // g_async_log.Start();
  xlcomm::Logger::SetOutputFunc(AsyncLogOutput);
  xlcomm::Logger::SetFlushFunc(AsyncLogFlush);
  testLog();
  // g_async_log是全局变量不用担心析构自动Stop
  // std::this_thread::sleep_for(std::chrono::seconds(1));
}

BOOST_AUTO_TEST_CASE(FixBuffer) {
  std::cout << "-------------FixBuffer\n";
  using Buffer = xlcomm::FixBuffer<10>;
  using BufferPtr = std::unique_ptr<Buffer>;

  BufferPtr buffer1(new Buffer());
  char str[] = "helloa";
  buffer1->Append(str, strlen(str));
  BOOST_CHECK(buffer1->used() == 6);
  BOOST_CHECK(buffer1->AvailSize() == 4);

  std::vector<BufferPtr> buffers;
  buffers.push_back(std::move(buffer1));
  BOOST_CHECK(buffer1 == nullptr);

  BufferPtr new_buffer(new Buffer);
  new_buffer->Reset();
  buffers.push_back(std::move(new_buffer));
  for (auto& buff : buffers) {
    std::cout << buff->data() << std::endl;
  }

  std::cout << "before resize\n";
  buffers.resize(1);  // 这里调用一次析构
  std::cout << "after resize\n";
  buffers.clear();  // 这里调用vector剩余元素析构
  std::cout << "after clear\n";

  buffer1.reset(new Buffer);
  BOOST_CHECK(buffer1->used() == 0);
}